package com.android.nextBus.Activities;

import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;


import android.app.ListActivity;
import android.content.Context;
import android.content.Intent;
import android.location.Criteria;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.os.Bundle;
import android.text.Editable;
import android.text.TextWatcher;
import android.view.Menu;
import android.view.View;
import android.view.Window;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;

import com.android.nextBus.DB.SQLiteOperationsWrapper;
import com.android.nextBus.DB.StopItem;
import com.android.nextBus.JSON.BaseServicesURL;
import com.android.nextBus.JSON.JSONBusStopHTTPGet;
import com.android.nextBus.JSON.RouteParser;
import com.google.android.maps.GeoPoint;

public class ViewStopsActivity extends ListActivity implements LocationListener {

	public static final String STOP_ID_KEY = "stop_id";
	public static final String STOP_NAME_KEY = "stop_name";
	public static final String STOP_LONG_KEY = "stop_lon_key";
	public static final String STOP_LAT_KEY = "stop_lat_key";
	private int routeID;
	private String routeName;
	private List<StopItem> stops;
	private int[] stopIDs;
	private String[] stopNames;
	private int[] stopLongs;
	private int[] stopLats;
	private Context ct;
	SQLiteOperationsWrapper db;
	
	private ProgressBar progess ; 
	private TextView txtLoading ;
	private TextView stops_header ;
	private Button stops_button;
	
	private ArrayAdapter<StopItem> mAdapter;
	private EditText filterText = null;
	
	private GeoPoint loc;

	
	LocationManager locationManager;
	String provider;
	Location location;
	
	
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        ct = this;
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        setContentView(R.layout.stops_layout);
		stops_header = (TextView) findViewById(R.id.stops_header);
		stops_button = (Button)findViewById(R.id.getNearestStopButton);
		stops_button.setClickable(false);
		stops_button.setEnabled(false);
		progess = (ProgressBar) findViewById(R.id.ProgressBar); 
		txtLoading = (TextView) findViewById(R.id.loading);
		
		filterText = (EditText) findViewById(R.id.search_box);
//		filterText.setText("");
//	    filterText.addTextChangedListener(filterTextWatcher);
		
		progess.setVisibility(View.VISIBLE);  
		txtLoading.setVisibility(View.VISIBLE); 
		filterText.setVisibility(View.GONE);
		
		// set up location components
		locationManager = (LocationManager) this.getSystemService(LOCATION_SERVICE);
		Criteria criteria = new Criteria();
		provider = locationManager.getBestProvider(criteria, false);
		location = locationManager.getLastKnownLocation(provider);
		
		//populate the list in a new thread so that we dont block the UI thread
        start();
		
    }
    
    
    /**
	 * Starts a new thread for going through the cursor 
	 * and populating the list with items 
	 * This method is used to spawn a new hidden 
	 * thread witch calls the runWork method
	 */
	public void start() {
		Runnable r = new Runnable() {
			public void run() {
				runWork();
			}
		};
		Thread t = new Thread(r);
		t.start();
	}

	
	/**
	 * Work to be done in the Thread.
	 */
	public void runWork() {
		
		// initialize and open the db
		db = new SQLiteOperationsWrapper(this);
		db.open();
		
		// the routeID and routeName from the server to be used for the header
		// and server request
		routeID = getIntent().getIntExtra(ViewRoutesActivity.ROUTE_ID_KEY, -1);
		routeName = getIntent().getStringExtra(ViewRoutesActivity.ROUTE_NAME_KEY);
		
		// if the stops are null get them from the db otherwise they were set
		// by the get nearest stops button
		if(stops == null) {
			stops = db.getStops(routeID);
		}
		
		// if stops are still null get them from the server
		if(stops == null) {
			
			String base = new BaseServicesURL().getRouteURL();
			
			
			String data = JSONBusStopHTTPGet.readBusStopJSONFeed(base + routeID,this.getIntent());
			if(this.getIntent().getExtras().getBoolean("failedGetRequest")) {
				
	
				System.out.println("Failed to http get");
				runOnUiThread(new Runnable() {   
					public void run(){  
				Toast.makeText(ct, "There was an error, Please check your data connection", Toast.LENGTH_LONG).show();
					} 
				});

				Intent intent2 = new Intent(this ,MainMenuActivity.class);
				startActivity(intent2);
				finish();
				db.close();
				return;
			} else {
			stops = RouteParser.parse(data);
		
			for(int i = 0; i < stops.size(); i++) {
				db.insertStopRow(stops.get(i).getStopName(), stops.get(i).getStopDesc(), 
						stops.get(i).getStopLat(), stops.get(i).getStopLon(), 
						stops.get(i).getStopID());
				
				db.insertRoutesStopsRow(routeID, stops.get(i).getStopID());
			}
			}
		}
		
		// close the db
		db.close();
		
		// initialize the list's item arrays used for intents
		stopIDs = new int[stops.size()];
		stopNames = new String[stops.size()];
		stopLats = new int[stops.size()];
		stopLongs = new int[stops.size()];

		// setup the list's item arrays used for intents
		for (int i = 0; i < stops.size(); i++) {
			stopIDs[i] = stops.get(i).getStopID();
			stopNames[i] = stops.get(i).getStopName() + ": "
					+ stops.get(i).getStopDesc();
			stopLats[i] = (int) stops.get(i).getStopLat();
			stopLongs[i] = (int) stops.get(i).getStopLon();
		}

		// Setup array adapter for the ListView
		mAdapter = new ArrayAdapter<StopItem>(this, R.layout.route_row, stops);
	    filterText.addTextChangedListener(filterTextWatcher);
	    runOnUiThread(new Runnable() {
			public void run() {
				// looks like it does nothing, but it pings the TextWatcher to 
				// force the list to be filtered again if there is already text (orientation change)
				filterText.setText(filterText.getText());
			}
		});
	    
		
		
		runOnUiThread(new Runnable() {
			public void run() {
				setListAdapter(mAdapter);
				stops_header.setText(routeName);
				progess.setVisibility(View.GONE);
				txtLoading.setVisibility(View.GONE);
				filterText.setVisibility(View.VISIBLE);
			}
		});

		getListView().setOnItemClickListener(new OnItemClickListener() {

			@Override
			public void onItemClick(AdapterView<?> parent, View view,
					int position, long id) {

				StopItem selectedStop = (StopItem) parent
						.getItemAtPosition(position);

				Intent intent = new Intent(getApplicationContext(),
						ViewTimesActivity.class);

				intent.putExtra(STOP_ID_KEY, selectedStop.getStopID());
				
				if(stops_button.getText().equals("Show All Stops")) {
					String[] splitName = selectedStop.toString().split(" ");
					
					String name = "";
					
					for(int i = 1; i < splitName.length; i++) {
						name = name + splitName[i] + " ";
					}
					
					intent.putExtra(STOP_NAME_KEY, name);
				} else {					
					intent.putExtra(STOP_NAME_KEY, selectedStop.toString());
				}
				
				intent.putExtra(STOP_LAT_KEY, (int) selectedStop.getStopLat());
				intent.putExtra(STOP_LONG_KEY, (int) selectedStop.getStopLon());

				startActivity(intent);
			}
		});
		
		runOnUiThread(new Runnable() {
			public void run() {
		stops_button.setClickable(true);
		stops_button.setEnabled(true);
			}
		});
	}
	
	
	/**
	 * Changes the list to either show the closest 6 stops or all the stops
	 * @param v
	 */
	public void getNearestStopsOnClick(View v) {
		
		// clear the filter
		filterText.setText("");
		
		// if the text is "Get Nearest Stops" button pressing will get the nearest
		// stops by location.  Otherwise it will redisplay all stops
		if (stops_button.getText().toString().equals("Get Nearest Stops")) {
			
			Criteria criteria = new Criteria();
			provider = locationManager.getBestProvider(criteria, false);
			location = locationManager.getLastKnownLocation(provider);
			
			// create a list of StopLocations which are an object containing a
			// StopItem and the distance between it and the users location
			ArrayList<StopLocations> stopLocs = new ArrayList<StopLocations>();

			// if location is not null get the nearest 6 stops else display a message
			// to user saying their location services are not on
			if (location != null) {

				// get latitude and longitude of the users location
				int latitude = (int) (location.getLatitude() * 1000000);
				int longitude = (int) (location.getLongitude() * 1000000);

				// create a geopoint for the users location
				loc = new GeoPoint(latitude, longitude);
				
				// loop through the stops and get the distance between user and
				// the stop and add to the stopLocs list
				for (int i = 0; i < stops.size(); i++) {
					// create a geopoint for the stop
					int stopLat = (int) (stops.get(i).getStopLat());
					int stopLon = (int) (stops.get(i).getStopLon());
					GeoPoint stopGeo = new GeoPoint(stopLat, stopLon);
					
					// obtain the distance in miles between points
					float dist = getDistanceInMiles(stopGeo, loc);

					// add to the stopLocs list
					stopLocs.add(new StopLocations(stops.get(i), dist));
				}

				// sort the stopLocs list by distance
				Collections.sort(stopLocs, new Comparator<StopLocations>() {
					public int compare(StopLocations c1, StopLocations c2) {
						float a1 = c1.getDistance();
						float a2 = c2.getDistance();
						
						return (int)((a1 - a2)*1000.0);
					}
				});

				// re-initialize the global stops list
				stops = new ArrayList<StopItem>();

				// get the top 6 nearest locations and add them to the list as
				// well as changing the stop name to include the distance
				for (int i = 0; i < 6; i++) {
					StopItem aStop = stopLocs.get(i).getStopItem();
					aStop.setStopName(""
							+ new DecimalFormat("#.##").format(stopLocs.get(i)
									.getDistance()) + "mi:  "
							+ stopLocs.get(i).getStopItem().getStopName());
					stops.add(aStop);
				}
				
				// change button text
				stops_button.setText("Show All Stops");
				start();
			} else {
				// give user an error message that location services are off.
				Toast.makeText(this, R.string.location_error, Toast.LENGTH_LONG)
						.show();
			}
		} else {
			// reset the stops and change the label of the button
			stops = null;
			stops_button.setText("Get Nearest Stops");
			start();
		}
	}
	
	
	/**
	 * Gets the distance in Miles between two GeoPoints
	 * @param p1 first point
	 * @param p2 second point
	 * @return float representing the distance in miles between the points
	 */
	public float getDistanceInMiles(GeoPoint p1, GeoPoint p2) {
		
		// set up the lats and lons of the 2 points
	    double lat1 = ((double)p1.getLatitudeE6()) / 1e6;
	    double lng1 = ((double)p1.getLongitudeE6()) / 1e6;
	    double lat2 = ((double)p2.getLatitudeE6()) / 1e6;
	    double lng2 = ((double)p2.getLongitudeE6()) / 1e6;
	    
	    // distance object
	    float [] dist = new float[1];
	    
	    // get the distance between objects 
	    Location.distanceBetween(lat1, lng1, lat2, lng2, dist);
	    
	    // convert and return to miles
	    return dist[0] * 0.000621371192f;
	}

	private TextWatcher filterTextWatcher = new TextWatcher() {

	    public void afterTextChanged(Editable s) {
	    }

	    public void beforeTextChanged(CharSequence s, int start, int count,
	            int after) {
	    }

	    public void onTextChanged(CharSequence s, int start, int before,
	            int count) {
	    	if (mAdapter != null) {
	    		mAdapter.getFilter().filter(s);
	    	}
	    }

	};

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.activity_view_stops, menu);
        return true;
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        filterText.removeTextChangedListener(filterTextWatcher);
    }


	@Override
	public void onLocationChanged(Location location) {
		// TODO Auto-generated method stub
		int lat = (int) (location.getLatitude());
	    int lng = (int) (location.getLongitude());
	    loc = new GeoPoint(lat, lng);
	}


	@Override
	public void onProviderDisabled(String provider) {
		// TODO Auto-generated method stub
		
	}


	@Override
	public void onProviderEnabled(String provider) {
		// TODO Auto-generated method stub
		
	}


	@Override
	public void onStatusChanged(String provider, int status, Bundle extras) {
		// TODO Auto-generated method stub
		
	}
	
	 /* Request updates at startup */
	  @Override
	  protected void onResume() {
	    super.onResume();
	    locationManager.requestLocationUpdates(provider, 400, 1, this);
	  }

	  /* Remove the locationlistener updates when Activity is paused */
	  @Override
	  protected void onPause() {
	    super.onPause();
	    locationManager.removeUpdates(this);
	  }

}
